package cn.fanqinghong.basic.service.impl;

import cn.fanqinghong.basic.constants.BaseConstants;
import cn.fanqinghong.basic.dto.RegisterDto;
import cn.fanqinghong.basic.exception.BusinessException;
import cn.fanqinghong.basic.service.IVerifyCodeService;
import cn.fanqinghong.basic.util.SmsUtils;
import cn.fanqinghong.basic.util.StrUtils;
import cn.fanqinghong.basic.util.VerifyCodeUtils;
import cn.fanqinghong.user.domain.User;
import cn.fanqinghong.user.mapper.UserMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.concurrent.TimeUnit;

/**
 * FAN
 * 2022/6/15  16:53
 */
@Service
@Slf4j
public class VerifyCodeServiceImpl implements IVerifyCodeService {
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private UserMapper userMapper;

    /**
     * 获取图片验证码
     * @param key
     * @return
     */
    @Override
    public String imageCode(String key) {
        log.info(key);
        if(!StringUtils.hasLength(key)){
            throw new BusinessException("key不能为空！！！");
        }
//        (每次都是直接生成新的，但是要覆盖前面的)
        String code = StrUtils.getComplexRandomString(4);//生成4位的验证码
//        。保存redis，设置过期时间  key是什么==前端请求给我传递的UUID
        redisTemplate.opsForValue().set(
                key,//前端传递的UUID
                code, //图片中的码儿
                5,//过期时间
                TimeUnit.MINUTES);//过期时间的单位
//        。使用2D引擎把code画成图
//        。把图使用Base64编码成字符串
//        。返回Base64编码的字符串
        return VerifyCodeUtils.VerifyCode(100,38,code);//生成图片验证码
    }

    /**
     * 一：校验
     *  1.空校验
     *  2.校验图形验证码，根据imageCodeKey从redis获取图形验证码
     *   redisTmplate.opsOfValue.get(imageKeyCode)
     *   。获取不到 图形验证码过期，重新获取
     *   。获取到了，比对图形验证码是否正确
     * 	  - 不正确，提示输入图形验证码错误
     * 	  - 正确，往下走
     *  3.校验手机号是否注册过了
     *   。注册过了，滚去登录
     *   。没有注册过，往下走
     * 二：短信验证码逻辑
     *  1.根据手机号拼接key，159:register，从redis获取曾今可能发送的短信验证码
     *   1.1 获取到了
     *     1.1.1 校验是否过了一分钟重发时间 value  9527:232468742341657213
     * 	   。小于一分钟  哥屋恩
     * 	   。大于一分中 （还没有到3分钟，验证码还有效），
     * 	       使用还没过期的验证码，刷新过期时间
     *   1.2 获取不到
     *      1.2.1.生成新的短信验证码
     *
     *  3.存储redis 设置一个过期时间
     *  4.发送短信
     * @param dto
     */
    @Override
    public void smsCode(RegisterDto dto) {
        String type = dto.getType();
        //非空判断
        if (StringUtils.isEmpty(type)){
            throw new BusinessException("type参数不能为空!!!");
        }
        //判断是注册还是绑定
        if (type.equals(BaseConstants.RegisterConstants.LOGIN_SMS_TYPE)){
            //如果类型是register就是注册
            registerSmsCode(dto);
        }

        if (type.equals(BaseConstants.WechatConstants.BINDER)){
            //如果类型是binder就是验证
            binderSmsCode(dto);
        }

    }



    /**
     * 注册
     * @param dto
     */
    private void registerSmsCode(RegisterDto dto) {
        //获得到图形验证码的key
        String imageCodeKey = dto.getImageCodeKey();
        //获得输入的手机号
        String phone = dto.getPhone();
        //输入的图形验证码的值
        String imageCodeValue = dto.getImageCodeValue();
//        *
//         * 一：校验
//         *  1.空校验
        if (imageCodeKey.isEmpty() || phone.isEmpty() ||imageCodeValue.isEmpty()){
            throw new BusinessException("参数不能为空，请检查参数");
        }
//         *
//         *  2.校验图形验证码，根据imageCodeKey从redis获取图形验证码
        Object imageCodeTmp = redisTemplate.opsForValue().get(imageCodeKey);
//         *   。获取不到 图形验证码过期，重新获取
        if (imageCodeTmp == null){
            throw new BusinessException("验证码已经过期，请重新获取!!!");
        }
//         *   。获取到了，比对图形验证码是否正确
        if (!imageCodeTmp.toString().equalsIgnoreCase(imageCodeValue)){
            //         * 	  - 不正确，提示输入图形验证码错误
            throw new BusinessException("图像验证码不正确，请输入正确的图形验证码!!");
        }
        //         * 	  - 正确，往下走
//         *  3.校验手机号是否注册过了
        User phoneTmp = userMapper.loadByPhone(phone);
//         *   。注册过了，滚去登录
        if (phoneTmp != null){
            throw new BusinessException("该用户已经存在，请直接登录!!");
        }
        //         *  1.根据手机号拼接key，register:159，从redis获取曾今可能发送的短信验证码
        String key = BaseConstants.RegisterConstants.REGISTER_SMS_PREFIX + phone;
        sendSmsCode(key);
    }


    /**
     * 绑定
     * @param dto
     */
    private void binderSmsCode(RegisterDto dto) {
        String key = BaseConstants.WechatConstants.BINDER_SMS_PREFIX + dto.getPhone();
        sendSmsCode(key);
    }

    /**
     * 发送手机验证码
     * @param key
     */
    private void sendSmsCode(String key) {
//          获取手机号
        String phone = key.split(":")[1];
//         *   。没有注册过，往下走
//         * 二：短信验证码逻辑
//         *  1.根据手机号拼接key，register:159，从redis获取曾今可能发送的短信验证码
        Object smsCodeTmp = redisTemplate.opsForValue().get(key);
        String smsCode = "";
        if (smsCodeTmp != null){
//         *   1.1 获取到了
//         *     1.1.1 校验是否过了一分钟重发时间 value  9527:232468742341657213
            Long oldTime = Long.valueOf(smsCodeTmp.toString().split(":")[1]);
            if (System.currentTimeMillis() - oldTime <= 1*60*1000){
                throw new BusinessException("小伙子我看你是要进局子啊");
            }
//         * 	   。小于一分钟  哥屋恩
//         * 	   。大于一分中 （还没有到3分钟，验证码还有效），
//         * 	       使用还没过期的验证码，刷新过期时间
            smsCode = smsCodeTmp.toString().split(":")[0];
        }else{
//         *   1.2 获取不到
//         *      1.2.1.生成新的短信验证码
            smsCode = StrUtils.getComplexRandomString(4);
        }
//         *
//         *  3.存储redis 设置一个过期时间
        String smsCodeValue = smsCode + ":" + System.currentTimeMillis();
        redisTemplate.opsForValue().set(
                key,//使用新生成的拼接的电话号码作为key
                smsCodeValue,//拼接了时间戳的验证码
                3,//三分钟过期时间
                TimeUnit.MINUTES
        );
//         *  4.发送短信
//         * @param dto
        SmsUtils.sendSms(phone,"亲，您的验证码是："+smsCode+"。请在3分钟内使用！！");
        System.out.println(key + "亲，您的验证码是："+smsCode+"。请在3分钟内使用！！");
    }


}
